#include "lcd.h"

void init_lcd(struct lcd_info *lcdinfo)
{
    lcdinfo->fd = open("/dev/fb0", O_RDWR);
    if(lcdinfo->fd == -1)
    {
        perror("打开lcd设备失败");
        exit(0);
    }

    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    // 获取lcd硬件参数
    ioctl(lcdinfo->fd, FBIOGET_VSCREENINFO, &vinfo);
    ioctl(lcdinfo->fd, FBIOGET_FSCREENINFO, &finfo);

    lcdinfo->width = vinfo.xres;    
    lcdinfo->height= vinfo.yres;    
    lcdinfo->bpp   = vinfo.bits_per_pixel;    
    lcdinfo->screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel/8;

    // 将映射内存设置为 800 * 480
    lcdinfo->fb_mem = mmap(NULL, lcdinfo->screensize*3, PROT_READ|PROT_WRITE,
                           MAP_SHARED, lcdinfo->fd, 0);
    if(lcdinfo->fb_mem == MAP_FAILED)
    {
        perror("内存映射失败");
        exit(0);
    }
}
// 清屏，白屏
void clean_screen(struct lcd_info *lcdinfo,int offsize)
{
    int white=0x00FFFFFF;
    // int screensize = (lcdinfo->width * lcdinfo->height * lcdinfo->bpp/8)*offsize;
    for(int i=lcdinfo->screensize*offsize;i<lcdinfo->screensize*(offsize+1);i+=4)
        memcpy(lcdinfo->fb_mem+i, &white, 4);
}

//关闭屏幕映射
void release_lcd(struct lcd_info *lcdinfo)
{
    if(lcdinfo == NULL)
        return;
    else
    {
        munmap(lcdinfo->fb_mem, 800*480*4);
        lcdinfo->fb_mem=NULL;
    }
    if(lcdinfo->fd!=-1)
    {
        close(lcdinfo->fd);
        lcdinfo->fd=-1;
    }
    
}
void lcd_input(struct lcd_info *lcdinfo,struct img_info *imginfo,int y)
{
    // clean_screen(lcdinfo,y);
    if(strstr(imginfo->name,".bmp"))
    {
        show_bmp(lcdinfo,imginfo,y);
    }
    if(strstr(imginfo->name,".jpg"))
    {
        show_jpg(lcdinfo,imginfo,y);
    }
    if(strstr(imginfo->name,".png"))
    {
        show_png(lcdinfo,imginfo,y);
    }
}
//改变可见区
void change_display(struct lcd_info *lcdinfo,int y)
{
    struct fb_var_screeninfo vinfo;
    vinfo.xoffset=0;
    vinfo.yoffset=480*y;
    ioctl(lcdinfo->fd,FBIOPAN_DISPLAY,&vinfo);
}
void init_display(struct lcd_info *lcdinfo,piclink head)
{
    change_display(lcdinfo,0);//一开始可见区为0号位置
    load_img(head);//对第一个图片进行解码
    lcd_input(lcdinfo,head->data,1);
    
    change_display(lcdinfo,1);//切换可见区

    load_img(head->prev);
    lcd_input(lcdinfo,head->prev->data,0);
    load_img(head->next);
    lcd_input(lcdinfo,head->next->data,2);


}
//滑屏操作
void switch_display(struct lcd_info *lcdinfo,piclink head,int tp)
{
    piclink p=head;
    bool flag=true;
    enum direction dir;
    int Y=1;
    while(flag)
    {
        dir=touchPanel(tp);
        if(dir==right)
        {
            Y=(Y-1+3)%3;
            change_display(lcdinfo,Y);
            free(p->next->data->rgb);
            load_img(p->prev->prev);
            lcd_input(lcdinfo,p->prev->prev->data,(Y-1+3)%3);
            p=p->prev;
        }
        else if(dir==left)
        {
            Y=(Y+1)%3;
            change_display(lcdinfo,Y);
            free(p->prev->data->rgb);
            load_img(p->next->next);
            lcd_input(lcdinfo,p->next->next->data,(Y+1)%3);
            p=p->next;
        }
        else if(dir==up)
        {
            free(p->data->rgb);
            free(p->next->data->rgb);
            free(p->prev->data->rgb);
            flag=false;
        }
    }
}
//对图片大小进行重新调整赋值
void scale_image(const char *jpeg_buffer, int original_width, int original_height,
                 char *scaled_buffer, int scaled_width, int scaled_height, int rgb_size) 
{
    // 检查缩放尺寸是否有效
    if (scaled_width <= 0 || scaled_height <= 0) {
        return;
    }

    float x_scale = (float)original_width / scaled_width;
    float y_scale = (float)original_height / scaled_height;
    printf("(%f,%f)\n",x_scale,y_scale);

    for (int y = 0; y < scaled_height; ++y) {
        for (int x = 0; x < scaled_width; ++x) {
            // 计算原始图像中对应的x和y坐标
            float orig_x = x * x_scale;
            float orig_y = y * y_scale;

            // 使用最近邻插值
            int ix = (int)orig_x;
            int iy = (int)orig_y;

            //确保坐标在原始图像范围内
            ix = ix < 0 ? 0 : (ix >= original_width ? original_width - 1 : ix);
            iy = iy < 0 ? 0 : (iy >= original_height ? original_height - 1 : iy);

            // 计算原始图像中当前像素的偏移量
            int offset = (iy * original_width + ix) * rgb_size;

            // 将原始图像中的RGB值复制到缩放后的图像中
            int scaled_offset = (y * scaled_width + x) * rgb_size;
            for (int c = 0; c < rgb_size; ++c) {
                scaled_buffer[scaled_offset + c] = jpeg_buffer[offset + c];
            }
            
        }
    }
}

//输出png格式图片
void show_png(struct lcd_info *lcdinfo,struct img_info *pnginfo,int offsize)
{
    // 解码png，并获取图片信息

    //清屏
    clean_screen(lcdinfo,offsize);

    unsigned long png_height=pnginfo->height;
    unsigned long png_width=pnginfo->width;
    float scale_factor;
    if(lcdinfo->width<pnginfo->width||lcdinfo->height<pnginfo->height)
    {
        float scale_width=(float)lcdinfo->width/pnginfo->width;
        float scale_height=(float)lcdinfo->height/pnginfo->height;
        scale_factor=(scale_height<scale_width)?scale_height:scale_width;
        png_height=(unsigned long)(pnginfo->height*scale_factor);
        png_width=(unsigned long)(pnginfo->width*scale_factor);
    }
    printf("png_width:%d, png_height:%d\n",png_width,png_height);

    int x = 0, y = 0;
    //居中处理
    if (lcdinfo->width > png_width || lcdinfo->height > png_height)
    {
        x = lcdinfo->width > png_width ? (lcdinfo->width - png_width) / 2 : 0;
        y = lcdinfo->height > png_height ? (lcdinfo->height - png_height) / 2 : 0;
    }
    printf("x:%d, y:%d\n",x,y);
    char *scaled_buffer=calloc(1,png_width*png_height*pnginfo->bpp/8);
    scale_image(pnginfo->rgb, pnginfo->width, pnginfo->height, scaled_buffer, png_width, png_height, (lcdinfo->bpp/8));

    char *tmp = lcdinfo->fb_mem + (800 * ((y > 0) ? (y - 1) : y) + x) * lcdinfo->bpp / 8;
    int wlcd = lcdinfo->width  - x * 2;
    int hlcd = lcdinfo->height - y * 2;

    for(int h = 0; h <pnginfo->height && h < hlcd; h++)
    {
        int lcd_offset = lcdinfo->width*4*h+lcdinfo->screensize*offsize;
        int rgb_offset = png_width*h*pnginfo->bpp/8;

        for(int w = 0; w < pnginfo->width && w < wlcd; w++)
        {
            memcpy(tmp + 4*w + lcd_offset+0, scaled_buffer +(pnginfo->bpp/8)*w+ rgb_offset + 2, 1);
            memcpy(tmp + 4*w + lcd_offset+1, scaled_buffer +(pnginfo->bpp/8)*w+ rgb_offset + 1, 1);
            memcpy(tmp + 4*w + lcd_offset+2, scaled_buffer +(pnginfo->bpp/8)*w+ rgb_offset + 0, 1);
        }
        // printf("%d\n",h);
    }
    free(scaled_buffer);
}

//输出jpg格式的图片
void show_jpg(struct lcd_info *lcdinfo, struct img_info *jpginfo,int offsize)
{
    // 加载图片信息
    // struct img_info *jpginfo = calloc(1, sizeof(struct img_info));
    // load_jpg(filename, jpginfo);

    // 清屏
    clean_screen(lcdinfo,offsize);
    unsigned long jpg_height=jpginfo->height;
    unsigned long jpg_width=jpginfo->width;
    float scale_factor;
    //对大图按比例调整图片大小
    if(lcdinfo->width<jpginfo->width||lcdinfo->height<jpginfo->height)
    {
        float scale_width=(float)lcdinfo->width/jpginfo->width;
        float scale_height=(float)lcdinfo->height/jpginfo->height;
        scale_factor=(scale_height<scale_width)?scale_height:scale_width;
        jpg_height=(unsigned long)(jpginfo->height*scale_factor);
        jpg_width=(unsigned long)(jpginfo->width*scale_factor);
    }
    printf("jpg_width:%d, jpg_height:%d\n",jpg_width,jpg_height);

    // 居中指针
    int x=0, y=0;
    if (lcdinfo->width > jpg_width || lcdinfo->height > jpg_height)
    {
        x = lcdinfo->width > jpg_width ? (lcdinfo->width - jpg_width) / 2 : 0;
        y = lcdinfo->height > jpg_height ? (lcdinfo->height - jpg_height) / 2 : 0;
    }
    printf("x:%d, y:%d\n",x,y);

    //分配新的空间用于存调整后的图片
    char *scaled_buffer = calloc(1,jpg_width * jpg_height * jpginfo->bpp/8);
    //用最近邻插值算法把原图数据按算法搬到新的空间
    scale_image(jpginfo->rgb,jpginfo->width,jpginfo->height,scaled_buffer,jpg_width,jpg_height, (jpginfo->bpp/8));
    

    char *tmp = lcdinfo->fb_mem + (800*((y>0)?(y-1):y) + x)*4;
    int wlcd = lcdinfo->width  - x*2;
    int hlcd = lcdinfo->height - y*2;


    for(int h=0; h<jpginfo->height && h<hlcd; h++)
    {
        int lcd_offset = lcdinfo->width * 4 * h+lcdinfo->screensize*offsize;
        int rgb_offset = jpg_width * 3 * h;

        // 显示一行
        for(int w=0; w<jpginfo->width && w<wlcd; w++)
        {
            memcpy(tmp + 4*w + lcd_offset+2, scaled_buffer + 3*w + rgb_offset+0, 1);
            memcpy(tmp + 4*w + lcd_offset+1, scaled_buffer + 3*w + rgb_offset+1, 1);
            memcpy(tmp + 4*w + lcd_offset+0, scaled_buffer + 3*w + rgb_offset+2, 1);
        }
        // printf("%d\n",h);
    }
    free(scaled_buffer);
}

//显示bmp格式的图片
void show_bmp(struct lcd_info *lcdinfo, struct img_info *bmpinfo,int offsize)
{
    // 加载图片信息
    // struct img_info *bmpinfo = calloc(1, sizeof(struct img_info));
    // load_bmp(filename, bmpinfo);
    
    // 清屏
    clean_screen(lcdinfo,offsize);
    unsigned long bmp_height=bmpinfo->height;
    unsigned long bmp_width=bmpinfo->width;
    float scale_factor;
    //对大图按比例调整图片大小
    if(lcdinfo->width<bmpinfo->width||lcdinfo->height<bmpinfo->height)
    {
        float scale_width=(float)lcdinfo->width/bmpinfo->width;
        float scale_height=(float)lcdinfo->height/bmpinfo->height;
        scale_factor=(scale_height<scale_width)?scale_height:scale_width;
        bmp_height=(unsigned long)(bmpinfo->height*scale_factor);
        bmp_width=(unsigned long)(bmpinfo->width*scale_factor);
    }
    printf("bmp_width:%d, bmp_height:%d\n",bmp_width,bmp_height);

    // 居中指针
    int x=0, y=0;
    if (lcdinfo->width > bmp_width || lcdinfo->height > bmp_height)
    {
        x = lcdinfo->width > bmp_width ? (lcdinfo->width - bmp_width) / 2 : 0;
        y = lcdinfo->height > bmp_height ? (lcdinfo->height - bmp_height) / 2 : 0;
    }
    printf("x:%d, y:%d\n",x,y);
    

    // 分配新的空间用于存调整后的图片
    char *scaled_buffer = calloc(1, bmp_width * bmp_height * bmpinfo->bpp / 8);
    // 用最近邻插值算法把原图数据按算法搬到新的空间
    scale_image(bmpinfo->rgb, bmpinfo->width, bmpinfo->height, scaled_buffer, bmp_width, bmp_height, (bmpinfo->bpp/8));

    char *tmp = lcdinfo->fb_mem + (800*((y>0)?(y-1):y) + x)*4;
    int wlcd = lcdinfo->width  - x*2;
    int hlcd = lcdinfo->height - y*2;
    
    // 计算无效字节
    // int linesize = bmpinfo->width*bmpinfo->bpp/8;
    int linesize = (bmp_width-1)*bmpinfo->bpp/8;
    int pad = (4-linesize%4) % 4; // 0 ~ 3

    // 取最底一行的指针
    // char *rgb = imginfo->rgb + (linesize+pad)*(bmpinfo->height-1);
    char *rgb = scaled_buffer + (linesize+pad)*(bmp_height-1);

    // 4-> 按照img的特性将RGB显示出来
    int w, h;
    for(w=0, h=0; h<bmpinfo->height && h<hlcd-1; h++)
    {
        unsigned lcd_offset = h * lcdinfo->width*4+lcdinfo->screensize*offsize;
        
        // 显示一行
        for(w=0; w<bmpinfo->width && w<wlcd; w++)
            memcpy(tmp + w*4 + lcd_offset, rgb + w*3 , 3);

        rgb -= (linesize + pad);
        // printf("%d\n",h);
    }
    free(scaled_buffer);
}